#include "lib-epoll.hh"
#include "sysexception.hh"
#include <sys/epoll.h>
#include <stdexcept>
#include <sstream>
#include <cerrno>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <sys/time.h>
#include <limits>

#define THROW_ME_NOW(EXCEPTION, ERRNO, WHAT) \
do { \
    std::ostringstream __what__; \
    __what__ << __FILE__ << ":" << __LINE__ << " - " << WHAT << " [" << __PRETTY_FUNCTION__ << "]"; \
    (EXCEPTION)(ERRNO, __what__.str()); \
} while (false)

#define THROW_NOW(EXCEPTION, WHAT) \
do { \
    std::ostringstream __what__; \
    __what__ << __FILE__ << ":" << __LINE__ << " - " << WHAT << " [" << __PRETTY_FUNCTION__ << "]"; \
    throw EXCEPTION(__what__.str()); \
} while (false)

#define THROW_RT(WHAT) THROW_NOW(std::runtime_error, WHAT)

namespace epoll
{

class exception:public std::runtime_error
{
public:
    exception(const std::string &_what): std::runtime_error(_what) { }
    exception(const exception &_src): std::runtime_error(_src) { }
    virtual ~exception() throw() { }
};

template < int error_code > class error:public exception
{
public:
    error(const std::string &_what): exception(_what) { }
    error(const error &_src): exception(_src) { }
    virtual ~error() throw() { }
    enum { ERRNO = error_code };
};

template < int v00,     int v01 = 0, int v02 = 0, int v03 = 0,
           int v04 = 0, int v05 = 0, int v06 = 0, int v07 = 0,
           int v08 = 0, int v09 = 0, int v10 = 0, int v11 = 0,
           int v12 = 0, int v13 = 0, int v14 = 0, int v15 = 0 > struct throw_exception_now
:   sys::throw_me_now< exception, error, v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13, v14, v15 >
{
};

typedef std::vector< struct ::epoll_event > array_of_event;

class manager
{
public:
    static manager& instance();
    enum event_value
    {
        EVENT_IN      = EPOLLIN,
        EVENT_OUT     = EPOLLOUT,
        EVENT_RDHUP   = EPOLLRDHUP,
        EVENT_PRI     = EPOLLPRI,
        EVENT_ERR     = EPOLLERR,
        EVENT_HUP     = EPOLLHUP,
        EVENT_ET      = EPOLLET,
        EVENT_ONESHOT = EPOLLONESHOT,
        EVENT_TIMEOUT = ~::uint32_t(0),
    };
    struct events
    {
        events(): value(0) { }
        events(enum event_value _value): value(_value) { }
        events(::uint32_t _value): value(_value) { }
        events(const struct events &_event): value(_event.value) { }
        operator ::uint32_t()const { return value; }
        struct events operator+(enum event_value _value)const { return value + ::uint32_t(_value); }
        struct events operator|(enum event_value _value)const { return value | ::uint32_t(_value); }
        struct events operator&(enum event_value _value)const { return value & ::uint32_t(_value); }
        events& operator=(const struct events &_event) { value = _event.value; return *this; }
        events& operator+=(const struct events &_event) { value += _event.value; return *this; }
        events& operator|=(const struct events &_event) { value |= _event.value; return *this; }
        events& operator&=(const struct events &_event) { value &= _event.value; return *this; }
        operator bool()const { return value != 0; }
        bool operator!()const { return value == 0; }
        ::uint32_t value;
    };
    typedef void (*event_handler)(void *_ctx, int _event_source, struct events _occurred);
    template < typename context > manager& registry(int _event_source, event_handler handler, context *_ctx,
                                                    struct events _watch);
    template < typename context > manager& registry(int _event_source, event_handler handler, context *_ctx,
                                                    struct events _watch, const struct ::timeval &_timeout_at);
    template < typename context > manager& modify(int _event_source, event_handler handler, context *_ctx,
                                                  struct events _watch);
    template < typename context > manager& modify(int _event_source, event_handler handler, context *_ctx,
                                                  struct events _watch, const struct ::timeval &_timeout_at);
    manager& release(int _event_source);
    manager& operator()();
    manager& operator()(const ::sigset_t &_sigmask);
    const struct ::timeval& now()const { return now_; }
private:
    manager();
    ~manager();
    enum operation
    {
        OP_ADD = EPOLL_CTL_ADD,
        OP_MOD = EPOLL_CTL_MOD,
        OP_DEL = EPOLL_CTL_DEL,
    };
    struct data
    {
        data() { }
        data(void *_ptr) { data_.ptr = _ptr; }
        data(int _fd) { data_.fd = _fd; }
        data(::uint32_t _u32) { data_.u32 = _u32; }
        data(::uint64_t _u64) { data_.u64 = _u64; }
        data(const union ::epoll_data &_data):data_(_data) { }
        data(const struct data &_src):data_(_src.data_) { }
        operator union ::epoll_data()const { return data_; }
        operator void*()const { return data_.ptr; }
        operator int()const { return data_.fd; }
        operator ::int32_t()const { return data_.u32; }
        operator ::int64_t()const { return data_.u64; }
        union ::epoll_data data_;
    };
    manager& ctl(operation _op, int _fd, struct ::epoll_event &_event);
    manager& ctl_add(int _fd, struct events _events, data _data);
    manager& ctl_mod(int _fd, struct events _events, data _data);
    manager& ctl_del(int _fd);
    manager& wait(array_of_event &_events, int _timeout_ms);
    manager& wait(array_of_event &_events, int _timeout_ms, const ::sigset_t &_sigmask);
    static void event_occurred(const struct ::epoll_event &_what);
    int fd_;
    struct watch
    {
        watch(event_handler _handler, void *_ctx): handler(_handler), ctx(_ctx) { }
        watch(const struct watch &_src): handler(_src.handler), ctx(_src.ctx) { }
        watch& operator=(const struct watch &_src) { handler = _src.handler; ctx = _src.ctx; return *this; }
        void operator()(int _source, struct events _occurred)const
        {
            handler(ctx, _source, _occurred);
        }
        event_handler handler;
        void *ctx;
    };
    struct watch_to : watch
    {
        static const struct ::timeval infinity;
        watch_to(event_handler _handler, void *_ctx, const struct ::timeval &_to = infinity):watch(_handler, _ctx), to(_to) { }
        watch_to(const struct watch &_src): watch(_src), to(infinity) { }
        watch_to(const struct watch_to &_src): watch(_src), to(_src.to) { }
        watch_to& operator=(const struct watch &_src) { this->watch::operator=(_src); return *this; }
        watch_to& operator=(const struct watch_to &_src) { this->watch::operator=(_src); to = _src.to; return *this; }
        bool is_infinite()const { return to.tv_sec == 0; }
        bool is_infinite(const struct ::timeval &_t) { return _t.tv_sec == 0; }
        struct ::timeval to;
    };
    typedef std::map< int, watch_to > event_source_watch;
    typedef std::set< int > set_of_event_source;
    struct timeval_cmp
    {
        bool operator()(const struct ::timeval &_a, const struct ::timeval &_b)const
        {
            return timercmp(&_a, &_b, <);
        }
    };
    typedef std::map< struct ::timeval, set_of_event_source, struct timeval_cmp > chronologic_timeout;
    event_source_watch event_source_watch_;
    chronologic_timeout chronologic_timeout_;
    struct ::timeval now_;
};

template < typename context >
manager& manager::registry(int _event_source, event_handler _handler, context *_ctx, struct events _watch)
{
    this->ctl_add(_event_source, _watch, _event_source);
    event_source_watch_[_event_source] = watch(_handler, reinterpret_cast< void* >(_ctx));
    return *this;
}

template < typename context >
manager& manager::registry(int _event_source, event_handler _handler, context *_ctx, struct events _watch,
                           const struct ::timeval &_timeout_at)
{
    this->ctl_add(_event_source, _watch, _event_source);
    watch_to watcher(_handler, reinterpret_cast< void* >(_ctx), _timeout_at);
    event_source_watch_[_event_source] = watcher;
    if (!watcher.is_infinite()) {
        chronologic_timeout_[_timeout_at].insert(_event_source);
    }
    return *this;
}

template < typename context >
manager& manager::modify(int _event_source, event_handler _handler, context *_ctx, struct events _watch)
{
    this->ctl_mod(_event_source, _watch, _event_source);
    event_source_watch_[_event_source] = watch(_handler, reinterpret_cast< void* >(_ctx));
    return *this;
}

template < typename context >
manager& manager::modify(int _event_source, event_handler _handler, context *_ctx, struct events _watch,
                         const struct ::timeval &_timeout_at)
{
    this->ctl_mod(_event_source, _watch, _event_source);
    watch_to &watcher = event_source_watch_[_event_source];
    if (watcher.is_infinite()) {       // timeout was infinite
        watcher = watch_to(_handler, reinterpret_cast< void* >(_ctx), _timeout_at);
        if (!watcher.is_infinite()) {  // timeout will be finite
            chronologic_timeout_[_timeout_at].insert(_event_source);
        }
        return *this;
    }
    // timeout was finite
    const struct ::timeval old_to = watcher.to;
    watcher = watch_to(_handler, reinterpret_cast< void* >(_ctx), _timeout_at);
    if (timercmp(&old_to, &_timeout_at, ==)) { // timeouts are the same
        return *this;
    }
    chronologic_timeout::iterator pOldSources = chronologic_timeout_.find(old_to);
    pOldSources->second.erase(_event_source);
    if (pOldSources->second.empty()) {
        chronologic_timeout_.erase(pOldSources);
    }
    if (!watcher.is_infinite()) { // timeout will be finite
        chronologic_timeout_[_timeout_at].insert(_event_source);
    }
    return *this;
}

manager& manager::release(int _event_source)
{
    event_source_watch::iterator pWatch = event_source_watch_.find(_event_source);
    if (!pWatch->second.is_infinite()) {
        chronologic_timeout::iterator pOldSources = chronologic_timeout_.find(pWatch->second.to);
        pOldSources->second.erase(_event_source);
        if (pOldSources->second.empty()) {
            chronologic_timeout_.erase(pOldSources);
        }
    }
    event_source_watch_.erase(_event_source);
    this->ctl_del(_event_source);
}

manager& manager::operator()()
{
    enum { INFINITY = -1 };
    array_of_event occurred_events;
    occurred_events.reserve(event_source_watch_.size());
    if (chronologic_timeout_.empty()) {
        this->wait(occurred_events, INFINITY);
        for (array_of_event::const_iterator pEvent = occurred_events.begin(); pEvent != occurred_events.end(); ++pEvent) {
            manager::event_occurred(*pEvent);
        }
        return *this;
    }
    struct ::timeval dT;
    ::gettimeofday(&now_, NULL);
    timersub(&chronologic_timeout_.begin()->first, &now_, &dT);
    ::int64_t timeout_ms = ::int64_t(1000) * dT.tv_sec + dT.tv_usec / 1000;
    if (timeout_ms < 0) {
        timeout_ms = 0;
    }
    else if (std::numeric_limits< int >::max() < timeout_ms) {
        timeout_ms = INFINITY;
    }
    this->wait(occurred_events, timeout_ms);
}

manager& manager::operator()(const ::sigset_t &_sigmask)
{
    enum { INFINITY = -1 };
    array_of_event occurred_events;
    occurred_events.reserve(event_source_watch_.size());
    this->wait(occurred_events, INFINITY, _sigmask);
    for (array_of_event::const_iterator pEvent = occurred_events.begin(); pEvent != occurred_events.end(); ++pEvent) {
        manager::event_occurred(*pEvent);
    }
    return *this;
}

manager::manager()
:   fd_(::epoll_create1(EPOLL_CLOEXEC))
{
    enum { FAILURE = -1 };
    if (fd_ == FAILURE) {
        const int c_errno = errno;
        throw_exception_now<EINVAL, EMFILE, ENFILE, ENOMEM> e;
        THROW_ME_NOW(e, c_errno, "epoll_create1(EPOLL_CLOEXEC) failure: " << std::strerror(c_errno));
    }
}

manager::~manager()
{
    enum { INVALID_DESCRIPTOR = -1 };
    if (fd_ != INVALID_DESCRIPTOR) {
        ::close(fd_);
        fd_ = INVALID_DESCRIPTOR;
    }
}

manager& manager::instance()
{
    static manager singelton;
    return singelton;
}

manager& manager::ctl(operation _op, int _fd, struct ::epoll_event &_event)
{
    enum { SUCCESS = 0 };
    if (::epoll_ctl(fd_, int(_op), _fd, &_event) == SUCCESS) {
        return *this;
    }
    const int c_errno = errno;
    throw_exception_now< EBADF, EEXIST, EINVAL, ENOENT, ENOMEM, ENOSPC, EPERM > e;
    THROW_ME_NOW(e, c_errno, "epoll_ctl(" << fd_ << ", " << int(_op) << ", " << _fd << ", "
                                       "{" << _event.events << ", " << _event.data.ptr << "}) "
                             "failure: " << std::strerror(c_errno));
}

manager& manager::ctl_add(int _fd, struct events _events, data _data)
{
    struct ::epoll_event event = {_events,_data};
    return this->ctl(OP_ADD, _fd, event);
}

manager& manager::ctl_mod(int _fd, struct events _events, data _data)
{
    struct ::epoll_event event = {_events,_data};
    return this->ctl(OP_MOD, _fd, event);
}

manager& manager::ctl_del(int _fd)
{
    return this->ctl(OP_DEL, _fd, *reinterpret_cast< struct ::epoll_event* >(this));
}

manager& manager::wait(array_of_event &_events, int _timeout_ms)
{
    const int ret_val = ::epoll_wait(fd_, &_events.front(), _events.capacity(), _timeout_ms);
    enum { FAILURE = -1 };
    if (ret_val != FAILURE) {
        _events.assign(ret_val, _events.front());
        return *this;
    }
    const int c_errno = errno;
    throw_exception_now< EBADF, EFAULT, EINTR, EINVAL > e;
    THROW_ME_NOW(e, c_errno, "epoll_wait(" << fd_ << ", " << (&_events.front()) << ", " << _events.capacity() << ", " 
                                           << _timeout_ms << ") failure: " << std::strerror(c_errno));
}

manager& manager::wait(array_of_event &_events, int _timeout_ms, const ::sigset_t &_sigmask)
{
    const int ret_val = ::epoll_pwait(fd_, &_events.front(), _events.capacity(), _timeout_ms, &_sigmask);
    enum { FAILURE = -1 };
    if (ret_val != FAILURE) {
        _events.assign(ret_val, _events.front());
        return *this;
    }
    const int c_errno = errno;
    throw_exception_now< EBADF, EFAULT, EINTR, EINVAL > e;
    THROW_ME_NOW(e, c_errno, "epoll_pwait(" << fd_ << ", " << (&_events.front()) << ", " << _events.capacity() << ", " 
                                           << _timeout_ms << ", " << (&_sigmask) << ") "
                             "failure: " << std::strerror(c_errno));
}

void manager::event_occurred(const struct ::epoll_event &_what)
{
    event_source_watch::const_iterator pHandler = manager::instance().event_source_watch_.find(_what.data.fd);
    if (pHandler != manager::instance().event_source_watch_.end()) {
        pHandler->second(_what.data.fd, _what.events);
        return;
    }
}

const struct ::timeval manager::watch_to::infinity = { 0, 0};

}//namespace epoll
